package com.zoweunion.mechanic.util.task;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.zoweunion.mechanic.controller.BaseController;
import com.zoweunion.mechanic.dao.base.EnclosureDao;
import com.zoweunion.mechanic.plugins.redis.RedisUtil;
import com.zoweunion.mechanic.util.constants.Constants;
import com.zoweunion.mechanic.util.GPSUtil;
import com.zoweunion.mechanic.util.HttpClient;
import com.zoweunion.mechanic.util.JiGuangPushUtil;
import com.zoweunion.mechanic.util.constants.PushConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.awt.geom.Point2D;
import java.util.*;

/**
 * 车辆电子围栏报警通知
 * @author rentianfeng
 */
@Deprecated
@Component
public class CarEnclosurePushScheduleTask extends BaseController {
    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private EnclosureDao enclosureDao;

    @Autowired
    private JiGuangPushUtil jiGuangPushUtil;

    private static double EARTH_RADIUS = 6378138.0;

    public static final long EXPIRE_TIME = 3600;

    /**
     * 每30秒执行一次
     */
    // @Scheduled(cron = "*/20 * * * * ? ")
    private void configureTasks() {
        logBefore(logger,"开启围栏预警定时任务");
        //获取所有的电子围栏绑定的车辆
        List<Map<String, Object>> cars = enclosureDao.getEnclosureAllCar();
        for (Map<String, Object> map : cars) {
            //拼接参数
            String opcpathUrl = Constants.THINGS_URL_ONE + map.get("opc_path").toString();
            //调用新思联接口
            JSONArray thingsArry = JSONArray.parseArray(HttpClient.doGet(opcpathUrl,Constants.interface_account, Constants.unify_account));
            // 获取车辆经纬度并对其有效性验证
            if (thingsArry != null && thingsArry.size() > 0) {
                JSONObject thingsObj = (JSONObject) thingsArry.get(0);
                JSONArray opcArry = (JSONArray) thingsObj.get("OPCValueList");
                // 根据实时数据获取经纬度
                for (int i = 0; i < opcArry.size(); i++) {
                    JSONObject opcObj = (JSONObject) opcArry.get(i);
                    if (opcObj.get("ValueTag").equals("GPSlongitude")) {
                        map.put("longitude", opcObj.get("DataValue").toString());
                    }
                    if (opcObj.get("ValueTag").equals("GPSlatitude")) {
                        map.put("latitude", opcObj.get("DataValue").toString());
                    }
                }
                // 经纬度有效验证
                double[] doubles = GPSUtil.gps84_To_Gcj02(Double.valueOf(map.get("latitude").toString()),
                        Double.valueOf(map.get("longitude").toString()));
                if (doubles.length >= 2) {
                    if ((doubles[0] > 0.06 || doubles[0] < -0.06) && (doubles[1] > 0.06 || doubles[1] < -0.06)) {
                        // 经纬度有效处理
                        map.put("GPSlatitude", doubles[0]);
                        map.put("GPSlongitude", doubles[1]);
                        //判断redis里有没有此车辆上一次位置
                        if (!redisUtil.exists(map.get("c_id").toString())) {
                            //将本次实时位置传入redis
                            redisUtil.set(map.get("c_id").toString(),map,EXPIRE_TIME);
                            continue;
                        }
                        //根据车辆id获取电子围栏
                        List<Map<String, Object>> enclosures = enclosureDao.getEnclosureByCid(map);
                        //根据电子围栏获取所有的经纬度
                        List<Map<String, Object>> enclosurePolygons = enclosureDao.getAllEnclosurePolygonByEId(enclosures);
                        //取redis上一次位置数据
                        Map<String, Object> lngLatMap = (Map<String, Object>) redisUtil.get(map.get("c_id").toString());
                        //推送map
                        Map<String, Object> reqMap = new HashMap<>(16);
                        for (Map<String, Object> enclosure : enclosures) {
                            //获取电子围栏绑定的通知人
                            List<Map<String, Object>> enclosureNotifiers = this.enclosureDao.getEnclosureNotifierByEId(enclosure);
                            //如果围栏为圆形
                            if (Integer.parseInt(enclosure.get("type").toString()) == 1) {
                                //判断此时位置在不在圆形内
                                boolean isInCircle = isInCircle(Double.valueOf(map.get("GPSlongitude").toString()),Double.valueOf(map.get("GPSlatitude").toString()),
                                        Double.valueOf(enclosure.get("lng").toString()),Double.valueOf(enclosure.get("lat").toString()),enclosure.get("radius").toString());
                                //判断上一次位置在不在圆形内
                                boolean isLastInCircle = isInCircle(Double.valueOf(lngLatMap.get("GPSlongitude").toString()),Double.valueOf(lngLatMap.get("GPSlatitude").toString()),
                                        Double.valueOf(enclosure.get("lng").toString()),Double.valueOf(enclosure.get("lat").toString()),enclosure.get("radius").toString());
                                //驶入报警
                                if (isInCircle == true && isLastInCircle == false && Integer.parseInt(enclosure.get("entry_warning").toString()) == 0) {
                                    //驶入报警
                                    if (enclosureNotifiers.size() > 0) {
                                        for (Map<String, Object> enclosureNotifier : enclosureNotifiers) {
                                            reqMap.put("current_user_id", enclosure.get("create_user").toString());
                                            reqMap.put("alias", enclosureNotifier.get("user_id").toString());
                                            reqMap.put("msg", PushConstants.TEMPLATE_ELECTRIC_FENCE_DRIVE_IN);
                                            reqMap.put("s_id", enclosure.get("s_id").toString());
                                            reqMap.put("type", "7");
                                            reqMap.put("c_id", map.get("c_id").toString());
                                            reqMap.put("jump_url", "/message");
                                            jiGuangPushUtil.pushJumpUrl(reqMap);
                                        }
                                    } else {
                                        reqMap.put("current_user_id", enclosure.get("create_user").toString());
                                        reqMap.put("alias", enclosure.get("create_user").toString());
                                        reqMap.put("msg", PushConstants.TEMPLATE_ELECTRIC_FENCE_DRIVE_IN);
                                        reqMap.put("s_id", enclosure.get("s_id").toString());
                                        reqMap.put("type", "7");
                                        reqMap.put("c_id", map.get("c_id").toString());
                                        reqMap.put("jump_url", "/message");
                                        jiGuangPushUtil.pushJumpUrl(reqMap);
                                    }
                                    break;
                                } else if (isInCircle == false && isLastInCircle == true && Integer.parseInt(enclosure.get("out_warning").toString()) == 0) {
                                    //驶出报警
                                    if (enclosureNotifiers.size() > 0) {
                                        for (Map<String, Object> enclosureNotifier : enclosureNotifiers) {
                                            reqMap.put("current_user_id", enclosure.get("create_user").toString());
                                            reqMap.put("alias", enclosureNotifier.get("user_id").toString());
                                            reqMap.put("msg", PushConstants.TEMPLATE_ELECTRIC_FENCE_DRIVE_OUT);
                                            reqMap.put("s_id", enclosure.get("s_id").toString());
                                            reqMap.put("type", "7");
                                            reqMap.put("c_id", map.get("c_id").toString());
                                            reqMap.put("jump_url", "/message");
                                            jiGuangPushUtil.pushJumpUrl(reqMap);
                                        }
                                    } else {
                                        reqMap.put("current_user_id", enclosure.get("create_user").toString());
                                        reqMap.put("alias", enclosure.get("create_user").toString());
                                        reqMap.put("s_id", enclosure.get("s_id").toString());
                                        reqMap.put("msg", PushConstants.TEMPLATE_ELECTRIC_FENCE_DRIVE_OUT);
                                        reqMap.put("type", "7");
                                        reqMap.put("c_id", map.get("c_id").toString());
                                        reqMap.put("jump_url", "/message");
                                        jiGuangPushUtil.pushJumpUrl(reqMap);
                                    }
                                    break;
                                }
                            } else if (Integer.parseInt(enclosure.get("type").toString()) == 2 || Integer.parseInt(enclosure.get("type").toString()) == 3)  {
                                List<Double> lon = new ArrayList<>();
                                List<Double> lat = new ArrayList<>();
                                for (Map<String, Object> enclosurePolygon : enclosurePolygons) {
                                    if (enclosure.get("id").toString().equals(enclosurePolygon.get("e_id").toString())) {
                                        lon.add(Double.valueOf(enclosurePolygon.get("lng").toString()));
                                        lat.add(Double.valueOf(enclosurePolygon.get("lat").toString()));
                                    }
                                }
                                Double[] lonArr = new Double[lon.size()];
                                lon.toArray(lonArr);
                                Double[] latArr = new Double[lat.size()];
                                lat.toArray(latArr);
                                //判断此时位置在不在多边形内
                                boolean isInPolygon = isInPolygon(Double.valueOf(map.get("GPSlongitude").toString()),Double.valueOf(map.get("GPSlatitude").toString()),lonArr,latArr);
                                //判断上一次位置在不在多边形内
                                boolean isLastInPolygon = isInPolygon(Double.valueOf(lngLatMap.get("GPSlongitude").toString()),Double.valueOf(lngLatMap.get("GPSlatitude").toString()),lonArr,latArr);
                                //驶入报警
                                if (isInPolygon == true && isLastInPolygon == false && Integer.parseInt(enclosure.get("entry_warning").toString()) == 0) {
                                    //驶入报警
                                    if (enclosureNotifiers.size() > 0) {
                                        for (Map<String, Object> enclosureNotifier : enclosureNotifiers) {
                                            reqMap.put("alias", enclosureNotifier.get("user_id").toString());
                                            reqMap.put("current_user_id", enclosure.get("create_user").toString());
                                            reqMap.put("msg", PushConstants.TEMPLATE_ELECTRIC_FENCE_DRIVE_IN);
                                            reqMap.put("s_id", enclosure.get("s_id").toString());
                                            reqMap.put("type", "7");
                                            reqMap.put("c_id", map.get("c_id").toString());
                                            reqMap.put("jump_url", "/message");
                                            jiGuangPushUtil.pushJumpUrl(reqMap);
                                        }
                                    } else {
                                        reqMap.put("current_user_id", enclosure.get("create_user").toString());
                                        reqMap.put("alias", enclosure.get("create_user").toString());
                                        reqMap.put("msg", PushConstants.TEMPLATE_ELECTRIC_FENCE_DRIVE_IN);
                                        reqMap.put("type", "7");
                                        reqMap.put("s_id", enclosure.get("s_id").toString());
                                        reqMap.put("c_id", map.get("c_id").toString());
                                        reqMap.put("jump_url", "/message");
                                        jiGuangPushUtil.pushJumpUrl(reqMap);
                                    }
                                    break;
                                } else if (isInPolygon == false && isLastInPolygon == true && Integer.parseInt(enclosure.get("out_warning").toString()) == 0) {
                                    //驶出报警
                                    if (enclosureNotifiers.size() > 0) {
                                        for (Map<String, Object> enclosureNotifier : enclosureNotifiers) {
                                            reqMap.put("alias", enclosureNotifier.get("user_id").toString());
                                            reqMap.put("current_user_id", enclosure.get("create_user").toString());
                                            reqMap.put("msg", PushConstants.TEMPLATE_ELECTRIC_FENCE_DRIVE_OUT);
                                            reqMap.put("s_id", enclosure.get("s_id").toString());
                                            reqMap.put("type", "7");
                                            reqMap.put("c_id", map.get("c_id").toString());
                                            reqMap.put("jump_url", "/message");
                                            jiGuangPushUtil.pushJumpUrl(reqMap);
                                        }
                                    } else {
                                        reqMap.put("current_user_id", enclosure.get("create_user").toString());
                                        reqMap.put("alias", enclosure.get("create_user").toString());
                                        reqMap.put("msg", PushConstants.TEMPLATE_ELECTRIC_FENCE_DRIVE_OUT);
                                        reqMap.put("s_id", enclosure.get("s_id").toString());
                                        reqMap.put("type", "7");
                                        reqMap.put("c_id", map.get("c_id").toString());
                                        reqMap.put("jump_url", "/message");
                                        jiGuangPushUtil.pushJumpUrl(reqMap);
                                    }
                                    break;
                                }
                            } else if (Integer.parseInt(enclosure.get("type").toString()) == 4) {
                                //保存此围栏的坐标
                                List<Map<String, Object>> enclosurePolygonNew = new ArrayList<>();
                                for (Map<String, Object> enclosurePolygon : enclosurePolygons) {
                                    if (enclosure.get("id").toString().equals(enclosurePolygon.get("e_id").toString())) {
                                        enclosurePolygonNew.add(enclosurePolygon);
                                    }
                                }
                                Collections.sort(enclosurePolygonNew, new Comparator<Map<String,Object>>(){
                                    @Override
                                    public int compare(Map<String,Object> o1, Map<String,Object> o2){
                                        return  (Integer)o1.get("number") > (Integer)o2.get("number")?1:( ((Integer) o1.get("number")).equals((Integer)o2.get("number"))?0:-1);
                                    }
                                });
                                for (int i = 0; i < enclosurePolygonNew.size()-1; i++) {
                                    boolean intersection = intersection(Double.valueOf(map.get("GPSlongitude").toString()),Double.valueOf(map.get("GPSlatitude").toString()),
                                            Double.valueOf(lngLatMap.get("GPSlongitude").toString()),Double.valueOf(lngLatMap.get("GPSlatitude").toString()),
                                            Double.valueOf(enclosurePolygonNew.get(i).get("lng").toString()),Double.valueOf(enclosurePolygonNew.get(i).get("lat").toString()),
                                            Double.valueOf(enclosurePolygonNew.get(i+1).get("lng").toString()),Double.valueOf(enclosurePolygonNew.get(i+1).get("lat").toString()));
                                    if (intersection == true && (Integer.parseInt(enclosure.get("entry_warning").toString()) == 0 || Integer.parseInt(enclosure.get("out_warning").toString()) == 0)) {
                                        //驶入报警
                                        if (enclosureNotifiers.size() > 0) {
                                            for (Map<String, Object> enclosureNotifier : enclosureNotifiers) {
                                                reqMap.put("alias", enclosureNotifier.get("user_id").toString());
                                                reqMap.put("current_user_id", enclosure.get("create_user").toString());
                                                reqMap.put("msg", "请注意，你的车辆已通过电子围栏区域！");
                                                reqMap.put("s_id", enclosure.get("s_id").toString());
                                                reqMap.put("type", "7");
                                                reqMap.put("c_id", map.get("c_id").toString());
                                                reqMap.put("jump_url", "/message");
                                                jiGuangPushUtil.pushJumpUrl(reqMap);
                                            }
                                        } else {
                                            reqMap.put("current_user_id", enclosure.get("create_user").toString());
                                            reqMap.put("alias", enclosure.get("create_user").toString());
                                            reqMap.put("msg", "请注意，你的车辆已通过电子围栏区域！");
                                            reqMap.put("type", "7");
                                            reqMap.put("s_id", enclosure.get("s_id").toString());
                                            reqMap.put("c_id", map.get("c_id").toString());
                                            reqMap.put("jump_url", "/message");
                                            jiGuangPushUtil.pushJumpUrl(reqMap);
                                        }
                                        break;
                                    }
                                }
                            }
                        }
                        //更新redis位置
                        redisUtil.set(map.get("c_id").toString(),map,EXPIRE_TIME);
                    }
                }
            }
        }
        logAfter(logger);
    }


    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    /**
     * 通过经纬度获取距离(单位：米)
     *
     * @param lat1
     * @param lng1
     * @param lat2
     * @param lng2
     * @return
     */
    public static double getDistance(double lat1, double lng1, double lat2,
                                     double lng2) {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
                Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000) / 10000;
        return s;
    }

    /**
     * 判断一个点是否在圆形区域内
     */
    public static boolean isInCircle(double lng1, double lat1, double lng2, double lat2, String radius) {
        double distance = getDistance(lat1, lng1, lat2, lng2);
        double r = Double.parseDouble(radius);
        if (distance > r) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 判断是否在多边形区域内
     *
     * @param pointLon
     *            要判断的点的纵坐标
     * @param pointLat
     *            要判断的点的横坐标
     * @param lon
     *            区域各顶点的纵坐标数组
     * @param lat
     *            区域各顶点的横坐标数组
     * @return
     */
    public static boolean isInPolygon(double pointLon, double pointLat, Double[] lon,
                                      Double[] lat) {
        // 将要判断的横纵坐标组成一个点
        Point2D.Double point = new Point2D.Double(pointLon, pointLat);
        // 将区域各顶点的横纵坐标放到一个点集合里面
        List<Point2D.Double> pointList = new ArrayList<Point2D.Double>();
        double polygonPoint_x = 0.0, polygonPoint_y = 0.0;
        for (int i = 0; i < lon.length; i++) {
            polygonPoint_x = lon[i];
            polygonPoint_y = lat[i];
            Point2D.Double polygonPoint = new Point2D.Double(polygonPoint_x, polygonPoint_y);
            pointList.add(polygonPoint);
        }
        return isInPolygon(point, pointList);
    }

    /**
     * 判断点是否在多边形内
     * @Title: IsPointInPoly
     * @Description: TODO()
     * @param point 测试点
     * @param pts 多边形的点
     * @return
     * @return boolean
     * @throws
     */
    public static boolean isInPolygon(Point2D.Double point, List<Point2D.Double> pts){

        int N = pts.size();
        boolean boundOrVertex = true;
        int intersectCount = 0;//交叉点数量
        double precision = 2e-10; //浮点类型计算时候与0比较时候的容差
        Point2D.Double p1, p2;//临近顶点
        Point2D.Double p = point; //当前点

        p1 = pts.get(0);
        for(int i = 1; i <= N; ++i){
            if(p.equals(p1)){
                return boundOrVertex;
            }

            p2 = pts.get(i % N);
            if(p.x < Math.min(p1.x, p2.x) || p.x > Math.max(p1.x, p2.x)){
                p1 = p2;
                continue;
            }

            //射线穿过算法
            if(p.x > Math.min(p1.x, p2.x) && p.x < Math.max(p1.x, p2.x)){
                if(p.y <= Math.max(p1.y, p2.y)){
                    if(p1.x == p2.x && p.y >= Math.min(p1.y, p2.y)){
                        return boundOrVertex;
                    }

                    if(p1.y == p2.y){
                        if(p1.y == p.y){
                            return boundOrVertex;
                        }else{
                            ++intersectCount;
                        }
                    }else{
                        double xinters = (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
                        if(Math.abs(p.y - xinters) < precision){
                            return boundOrVertex;
                        }

                        if(p.y < xinters){
                            ++intersectCount;
                        }
                    }
                }
            }else{
                if(p.x == p2.x && p.y <= p2.y){
                    Point2D.Double p3 = pts.get((i+1) % N);
                    if(p.x >= Math.min(p1.x, p3.x) && p.x <= Math.max(p1.x, p3.x)){
                        ++intersectCount;
                    }else{
                        intersectCount += 2;
                    }
                }
            }
            p1 = p2;
        }
        if(intersectCount % 2 == 0){//偶数在多边形外
            return false;
        } else { //奇数在多边形内
            return true;
        }
    }

    /**两【线段】是否相交
     * @param l1x1 线段1的x1
     * @param l1y1 线段1的y1
     * @param l1x2 线段1的x2
     * @param l1y2 线段1的y2
     * @param l2x1 线段2的x1
     * @param l2y1 线段2的y1
     * @param l2x2 线段2的x2
     * @param l2y2 线段2的y2
     * @return 是否相交
     */
    public static boolean intersection(double l1x1, double l1y1, double l1x2, double l1y2,
                                       double l2x1, double l2y1, double l2x2, double l2y2)
    {
        // 快速排斥实验 首先判断两条线段在 x 以及 y 坐标的投影是否有重合。 有一个为真，则代表两线段必不可交。
        if (Math.max(l1x1,l1x2) < Math.min(l2x1 ,l2x2)
                || Math.max(l1y1,l1y2) < Math.min(l2y1,l2y2)
                || Math.max(l2x1,l2x2) < Math.min(l1x1,l1x2)
                || Math.max(l2y1,l2y2) < Math.min(l1y1,l1y2))
        {
            return false;
        }
        // 跨立实验  如果相交则矢量叉积异号或为零，大于零则不相交
        if ((((l1x1 - l2x1) * (l2y2 - l2y1) - (l1y1 - l2y1) * (l2x2 - l2x1))
                * ((l1x2 - l2x1) * (l2y2 - l2y1) - (l1y2 - l2y1) * (l2x2 - l2x1))) > 0
                || (((l2x1 - l1x1) * (l1y2 - l1y1) - (l2y1 - l1y1) * (l1x2 - l1x1))
                * ((l2x2 - l1x1) * (l1y2 - l1y1) - (l2y2 - l1y1) * (l1x2 - l1x1))) > 0)
        {
            return false;
        }
        return true;
    }

}
